home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 October: Mac OS SDK / Dev.CD Oct 97 SDK1.toast / Development Kits (Disc 1) / QuickDraw GX / Programming Stuff / Sample Code / Printing Samples / Extensions… / Additions ƒ / Additions.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-14  |  21.1 KB  |  758 lines  |  [TEXT/MPS ]

  1. /* ------------------------------------------------------------------------------
  2.  
  3.     FILENAME
  4.         Additions.c
  5.  
  6.     DESCRIPTION
  7.         This file contains the code that creates the graphical effects of the Additions printing
  8.         extension.  These routines allow the user to augment pages of a document with additive effects.
  9.         These effects include:
  10.         
  11.                     Adding a page border
  12.                     Serialize all printed copies
  13.                     Add cover page at beginning or end.
  14.                     
  15.         These options are configured through a Print dialog panel.  The following files
  16.         comprise the Additions printing extension:
  17.         
  18.         Additions.a                    contains jump table of printing message entry points into the extension
  19.         Additions.c                    contains the routines that create the additive graphic effects
  20.         Additions.make                make file for the Additions printing extension
  21.         Additions.r                    contains resource definitions for all resources associated with the extension
  22.         Additions.h                    header file for the Additions.c file
  23.         Utilities.c                    contains utility routines referenced by all other modules
  24.         Utilities.h                    header file for the Utilities.c file
  25.         DespoolPageMessage.c        contains entry point routine for processing the DespoolPage message
  26.         StartJobMessage.c            contains entry point routine for processing the StartJob message
  27.         FinishJobMessage.c        contains entry point routine for processing the FinishJob message
  28.         RenderPageMessage.c        contains entry point routine for processing the RenderPage message
  29.         PrintDialogMessage.c        contains entry point routine for processing the PrintDialog message
  30.         
  31.         The following table summarizes which printing messages the Additions extension supports
  32.         and what "additive effects" it can generate during these messages:
  33.         
  34.         DespoolPage message:
  35.             -    adds the page border to the document page
  36.             -    creates a serial number text shape to be used for serialization of document pages
  37.                 at page rendering time
  38.                 
  39.         StartJob message:
  40.             -    adds a cover page to the beginning of the document being printed
  41.             
  42.         FinishJob message:
  43.             -    adds a cover page to the end of the document being printed
  44.         
  45.         RenderPage message:
  46.             -    adds a serial number to the document pages being printed
  47.             
  48.         PrintDialog message:
  49.             -    adds the Addition's Print dialog panel to the Print dialog
  50.             -    creates, and attaches to the print Job, an Additions collection item which contains
  51.                 the specification of the graphic effects the user selected in the Additions dialog panel.
  52.                 The format of this collection item is specified by the AdditionsCollection type defined
  53.                 in the Additions.h file.
  54.             
  55.     COPYRIGHT
  56.         Copyright Apple Computer, Inc. 1991 - 1996
  57.         All rights reserved. 
  58.     
  59.     INTERFACE ROUTINES
  60.         ComposeCoverPage
  61.         AddPageBorder
  62.         CreateNumberShape
  63.         SerializeCopies
  64.  
  65.     MODIFICATION HISTORY
  66.         05/15/91            ALA                Initial Implementation
  67.          6/14/96            cn                    Updated to support Universal Interfaces 2.1.
  68.  
  69.  
  70. ------------------------------------------------------------------------------- */
  71.  
  72. #include <Types.h>
  73. #include <Quickdraw.h>
  74. #include <Memory.h>
  75. #include <Resources.h>
  76. #include <Dialogs.h>
  77. #include <TextEdit.h>
  78. #include <OSUtils.h>
  79. #include <Packages.h>
  80. #include <ToolUtils.h>
  81. #include <Menus.h>
  82. #include <String.h>
  83. #include <Strings.h>
  84. #include <Printing.h>
  85. #include <FixMath.h>
  86.  
  87. #include <GXGraphics.h>
  88. #include <GraphicsLibraries.h>
  89. #include <GXFonts.h>
  90. #include <FontLibrary.h>
  91.  
  92. #include <Collections.h>
  93. #include <GXMessages.h>
  94.  
  95. #include    <GXPrinting.h>
  96.  
  97. #include "Utilities.h"
  98. #include "Additions.h"
  99.  
  100.  
  101. /*==================================== CONSTANTS ====================================*/
  102.  
  103.  
  104. /* ======= Misc. Contants ======= */
  105.  
  106. #define    kUserNameStrID        -16096    /* Resource ID of system string containing the user's name */
  107.  
  108.  
  109. /*===================================== INTERNAL ROUTINES =====================================*/
  110.  
  111.  
  112. /*========================= INTERNAL ROUTINES FOR ADDING A COVER PAGE =========================*/
  113.  
  114.  
  115. /* ===== AddTextToCover =====
  116.  
  117.     AddTextToCover adds the specified text to the cover page picture and centers the
  118.     text within that picture.
  119. */
  120. OSErr AddTextToCover(        //    (out)    Error code
  121.     gxShape        coverPage,    //    (in)    picture representing the cover page
  122.     char            *textStr,    //    (in)    Text string to add to page (Pascal format)
  123.     gxPoint        *where)        //    (in)    Location where to place the text on the page
  124. {
  125.     OSErr            anErr = noErr;
  126.     gxShape        aShape;
  127.     
  128.     aShape = GXNewText( Length(textStr), (unsigned char const *) &textStr[1], where);
  129.     if (aShape != nil)
  130.     {
  131.         /* Set the shape's font and size */
  132.         GXSetShapeFont(aShape, FindPNameFont(gxFullFontName, "\pHelvetica"));
  133.         GXSetShapeTextSize(aShape, ff(14));
  134.     
  135.         /* Add the shape to the cover page */
  136.         AddToShape(coverPage, aShape);
  137.         GXDisposeShape(aShape);
  138.     }
  139.     else    /* A graphics error occurred creating the shape */
  140.     {
  141.         gxGraphicsError    stickyErr;
  142.         
  143.         anErr = GXGetGraphicsError(&stickyErr);
  144.     }
  145.  
  146.     return(anErr);
  147. }
  148. /* AddTextToCover */
  149.  
  150.  
  151. /* ===== AddCoverSplash =====
  152.  
  153.     AddCoverSplash adds a special cover page text message to the top of the page.
  154. */
  155. OSErr AddCoverSplash(        //    (out)    Error code
  156.     gxShape            coverPage,    //    (in)    picture representing the cover page
  157.     char                *textStr,    //    (in)    Text string to add to page (Pascal format)
  158.     gxRectangle        *pageRect,    //    (in)    Rectangle representing the cover page
  159.     gxPoint            *where)        //    (in)    Location where to place the text on the page
  160. {
  161.     OSErr                    anErr = noErr;
  162.     gxShape                aShape;
  163.     gxRectangle            shapeRect;
  164.     
  165.     aShape = GXNewText( Length(textStr), (unsigned char const *) &textStr[1], where);
  166.     if (aShape != nil)
  167.     {
  168.         /* Set the text's font and size */
  169.         GXSetShapeFont(aShape, FindPNameFont(gxFullFontName, "\pHelvetica"));
  170.         GXSetShapeTextSize(aShape, ff(48));
  171.     
  172.         /* Center the text (left to right) in the page */
  173.         GXGetShapeBounds(aShape, 0, &shapeRect);    /* Center the text (left-to-right) in the page */
  174.         GXMoveShapeTo(aShape, FixDiv((pageRect->right - pageRect->left) - (shapeRect.right - shapeRect.left), ff(2)) + pageRect->left, shapeRect.top);
  175.     
  176.         GXSetShapeType(aShape, gxPathType);
  177.         GXSetShapeFill(aShape, gxClosedFrameFill);
  178.         
  179.         AddToShape(coverPage, aShape);
  180.         GXDisposeShape(aShape);
  181.     }
  182.     else
  183.     {
  184.         gxGraphicsError        stickyErr;
  185.  
  186.         anErr = GXGetGraphicsError(&stickyErr);
  187.     }
  188.  
  189.     return(anErr);
  190. }
  191. /* AddCoverSplash */
  192.  
  193.  
  194. /*=================== INTERNAL ROUTINES FOR ADDING PAGE BORDER TO DOCUMENT PAGES ===================*/
  195.  
  196.  
  197. /* ===== RemoveAllShapes =====
  198.  
  199.     RemoveAllShapes removes all shapes from the specified picture.
  200. */
  201. void RemoveAllShapes(
  202.     gxShape        thePict)            //    (in)    picture whose shapes are to be removed
  203. {
  204.     gxShape            aShape;
  205.     long                numShapes;
  206.     gxShapeType        theType;
  207.  
  208.     numShapes = GXGetPicture(thePict, nil, nil, nil, nil);
  209.     for (; numShapes > 0; numShapes--)
  210.     {
  211.         GetPictureItem(thePict, 1, &aShape, nil, nil, nil);
  212.         theType = GXGetShapeType(aShape);
  213.  
  214.         if (theType == gxPictureType)    /* Call recursively to process this picture */
  215.         {
  216.             RemoveAllShapes(aShape);
  217.         }
  218.         GXDisposeShape( ExtractShape(thePict, 1, 1) );
  219.     }
  220. }
  221. /* RemoveAllShapes */
  222.  
  223.  
  224. /*======================== INTERFACE ROUTINES =======================*/
  225.  
  226.  
  227. /*========================= INTERFACE ROUTINES FOR ADDING A COVER PAGE =========================*/
  228.  
  229.  
  230. /* ===== ComposeCoverPage =====
  231.  
  232.     ComposeCoverPage creates a picture which represents the cover page of the printed
  233.     document.
  234. */
  235. OSErr ComposeCoverPage(                        //    (out)    Error code
  236.     gxShape                        *coverPage,        //    (out)    picture representing the cover page
  237.     gxFormat                        theFormat,        //    (in)    Cover page format reference
  238.     gxJobInfo                    *printerInfo)    //    (in)    Contains info. about the document being printed
  239. {
  240.     OSErr                    anErr = noErr;
  241.     gxRectangle            pageRect;
  242.     gxPoint                where;
  243.     gxGraphicsError    stickyErr;
  244.     
  245.     /* Allocate the picture shape that represents the cover page */
  246.     *coverPage = GXNewShape(gxPictureType);
  247.     if (*coverPage == nil)
  248.     {
  249.         anErr = GXGetGraphicsError(&stickyErr);
  250.         goto CantCreateCoverPage;
  251.     }
  252.     
  253.     /* Get the page rectangle from the format */
  254.     GXGetFormatDimensions(theFormat, &pageRect, nil);
  255.     
  256.     /* Make the picture's bounding rectangle = page rectangle, and pin at (0, 0) */
  257.     // GXSetShapeBounds(*coverPage, &pageRect);
  258.     // GXMoveShapeTo(*coverPage, ff(0), ff(0));
  259.     
  260.     /* Create a little splash message */
  261.         
  262.     where.y = pageRect.top + ff(100);
  263.     where.x = pageRect.left + ff(180);
  264.  
  265.     anErr = AddCoverSplash(*coverPage,    (char *) "\pCover Page", &pageRect, &where);
  266.     if (anErr != noErr)
  267.     {
  268.         goto CantAddCoverSplash;
  269.     }
  270.     
  271.     /* Create the <document name> shape */
  272.  
  273.     where.y += ff(150);
  274.     {
  275.         anErr = AddTextToCover(*coverPage,    (char *) "\pDocument:", &where);
  276.         if (anErr != noErr)
  277.         {
  278.             goto CantAddDocumentText;
  279.         }
  280.         
  281.         where.x += ff(100);
  282.         
  283.         anErr = AddTextToCover(*coverPage,    (char *) printerInfo->documentName, &where);
  284.         if (anErr != noErr)
  285.         {
  286.             goto CantAddDocumentName;
  287.         }
  288.     }
  289.     
  290.     /* Create the <application name> shape */
  291.  
  292.     where.y += ff(30);
  293.     where.x = pageRect.left + ff(180);
  294.     {
  295.         anErr = AddTextToCover(*coverPage,    (char *) "\pApplication:", &where);
  296.         if (anErr != noErr)
  297.         {
  298.             goto CantAddAppText;
  299.         }
  300.         
  301.         where.x += ff(100);
  302.  
  303.         anErr = AddTextToCover(*coverPage,    (char *) printerInfo->appName, &where);
  304.         if (anErr != noErr)
  305.         {
  306.             goto CantAddAppName;
  307.         }
  308.     }
  309.  
  310.     /* Create the <user name> shape */
  311.  
  312.     where.y += ff(30);
  313.     where.x = pageRect.left + ff(180);
  314.     {
  315.         anErr = AddTextToCover(*coverPage,    (char *) "\pUser:", &where);
  316.         if (anErr != noErr)
  317.         {
  318.             goto CantAddUserText;
  319.         }
  320.     
  321.         where.x += ff(100);
  322.     
  323.         anErr = AddTextToCover(*coverPage,    (char *) printerInfo->userName, &where);
  324.         if (anErr != noErr)
  325.         {
  326.             goto CantAddUserNameRsrc;
  327.         }
  328.     }
  329.  
  330.     /* Create the <date> shape */
  331.  
  332.     where.y += ff(30);
  333.     where.x = pageRect.left + ff(180);
  334.     {
  335.         unsigned long    secs;
  336.         Str255            s;
  337.         
  338.         anErr = AddTextToCover(*coverPage,    (char *) "\pDate:", &where);
  339.         if (anErr != noErr)
  340.         {
  341.             goto CantAddDateText;
  342.         }
  343.         
  344.         where.x += ff(100);
  345.  
  346.         GetDateTime(&secs);
  347.         IUDateString(secs, shortDate, s);
  348.         
  349.         anErr = AddTextToCover(*coverPage,    (char *) s, &where);
  350.         if (anErr != noErr)
  351.         {
  352.             goto CantAddDate;
  353.         }
  354.     }
  355.     
  356.     return(noErr);
  357.  
  358. /************** Clean-up **************/
  359.  
  360. CantAddDate:
  361. CantAddDateText:
  362. CantAddUserNameRsrc:
  363. CantAddUserName:
  364. CantAddUserText:
  365. CantAddAppName:
  366. CantAddAppText:
  367. CantAddDocumentName:
  368. CantAddDocumentText:
  369. CantAddCoverSplash:
  370.     GXDisposeShape(*coverPage);
  371.     
  372. CantCreateCoverPage:
  373.     
  374.     return(anErr);
  375. }
  376. /* ComposeCoverPage */
  377.  
  378.  
  379. /*=================== INTERFACE ROUTINES FOR ADDING PAGE BORDER TO DOCUMENT PAGES ===================*/
  380.  
  381.  
  382. /* ===== AddPageBorder =====
  383.  
  384.     AddPageBorder adds a border around the entire page and and scales the page to make sure
  385.     all shapes fit within the border.  The page to border is specified by the "page" parameter.
  386.     
  387.     To add the page border, the routine performs the following steps:
  388.         1.    Make a copy of the original page shape (newPage variable).
  389.         2.    Scale the newPage shape by approx. 80%.
  390.         3.    Scale any embedded bitmaps in the newPage shape. (graphics didn't handle this before).
  391.         4.    Remove all shapes from the original page shape (page variable).
  392.         5.    Add the newPage shape to the page shape (must preserve the original page referenced passed to us).
  393.         6.    Add the shapes to the page shape which comprise the border.
  394.     
  395.     Note: the original page shape is duplicated because we must scale the contents of the shape,
  396.     but not the shapes which comprise the border.  Previously, it didn't work to scale the page down
  397.     and scale the border up so it's bigger.
  398. */
  399. OSErr AddPageBorder(                //    (out)    Error code
  400.     gxShape        page,                //    (in)    picture for the page being spooled; we'll add a border to it
  401.     gxFormat        pageFormat,        //    (in)    Page format record
  402.     Str32            docName)            //    (in)    Name of the document being printed
  403. {
  404.     OSErr                            anErr = noErr;
  405.     gxRectangle                    pageBounds;
  406.     gxPoint                        pageCenter;
  407.     gxPoint                        ovalsize;
  408.     gxShape                        border;
  409.     gxShape                        newPage;
  410.     gxColor                        blackBorder;
  411.     gxGraphicsError            stickyErr;
  412.     gxShape                        nameShape;
  413.     gxShape                        userNameShape;
  414.     gxShape                        dateShape;
  415.     gxMapping                    pagemap;
  416.     gxMapping                    bordermap;
  417.     gxTransform                    invxform;
  418.     
  419.     
  420.     /* Create a new page shape */
  421.     newPage = page;
  422.  
  423.     /* Get the dimensions of the page so we can determine the center of the page */
  424.     GXGetFormatDimensions(pageFormat, &pageBounds, nil);
  425.     pageCenter.x = ( pageBounds.left + pageBounds.right ) >> 1;
  426.     pageCenter.y = ( pageBounds.top + pageBounds.bottom ) >> 1;
  427.  
  428.     /* Scale the picture by approx. 80% */
  429.     GXGetShapeMapping( newPage, &pagemap );
  430.     ScaleMapping( &pagemap, 0x0000CC11, 0x0000CC11, pageCenter.x, pageCenter.y);
  431.     GXSetShapeMapping( newPage, &pagemap );
  432.     InvertMapping( &bordermap, &pagemap );
  433.  
  434.     anErr = GXGetGraphicsError(&stickyErr);
  435.     if (anErr != noErr)
  436.     {
  437.         goto CantScalePage;
  438.     }
  439.     
  440.     /* Make an inverse transform for the rest of the items that we need to add */
  441.     invxform = GXNewTransform();    
  442.     GXSetTransformMapping( invxform, &bordermap );
  443.  
  444.     pageBounds.left += ff(2);
  445.     pageBounds.right -= ff(2);
  446.     pageBounds.top += ff(20);
  447.     pageBounds.bottom -= ff(20);
  448.  
  449.     ovalsize.x = ff(20);
  450.     ovalsize.y = ff(20);
  451.     
  452.     border = NewRoundRect(&pageBounds, &ovalsize);
  453.     anErr = GXGetGraphicsError(&stickyErr);
  454.     if (anErr != noErr)
  455.     {
  456.         goto CantCreateBorderRect;
  457.     }
  458.  
  459.     /* Make sure the round rectangle appears in black */
  460.     
  461.     blackBorder.space = gxGraySpace;
  462.     blackBorder.profile = nil;
  463.     blackBorder.element.gray = 0;
  464.     GXSetShapeColor(border, &blackBorder);
  465.  
  466.     /* Make sure the border fill is correct and is two points wide */
  467.     GXSetShapeFill(border, gxClosedFrameFill);
  468.     GXSetShapePen(border, ff(2));
  469.     
  470.     /* Add the border shape to the page shape */
  471.     GXSetPictureParts( page, 0, 0, 1, &border, nil, nil, &invxform );
  472.     GXDisposeShape(border);
  473.  
  474.     /* Create the header text items; first we add the document name.  The document name is centered */
  475.     /* at the top of the border. */
  476.     {
  477.         gxPoint        where;
  478.         
  479.         where.y = pageBounds.top - ff(5);
  480.         where.x = pageBounds.left;
  481.         
  482.         nameShape = GXNewText( Length(docName), &docName[1], &where);
  483.         if (nameShape != nil)    /* Set the item's font and position it correctly */
  484.         {
  485.             gxRectangle    shapeRect;
  486.             
  487.             GXSetShapeFont(nameShape, FindPNameFont(gxFullFontName, "\pSkia Regular"));
  488.             GXSetShapeTextSize(nameShape, ff(12));
  489.             {
  490.                 gxFontVariation        variations;
  491.                 
  492.                 variations.name = 'wght';
  493.                 variations.value = 0x00028000;
  494.                 
  495.                 GXSetStyleFontVariations(GXGetShapeStyle(nameShape), 1, &variations);
  496.             }
  497.             
  498.             /* Center the text on the page */
  499.             
  500.             GXGetShapeBounds(nameShape, 0, &shapeRect);
  501.             GXMoveShapeTo(nameShape, FixDiv((pageBounds.right - pageBounds.left) - (shapeRect.right - shapeRect.left), ff(2)) + pageBounds.left, where.y);
  502.  
  503.             /* Add the document name shape to the page */
  504.             GXSetPictureParts( page, 0, 0, 1, &nameShape, nil, nil, &invxform );
  505.             GXDisposeShape(nameShape);
  506.         }
  507.         else
  508.         {    
  509.             anErr = GXGetGraphicsError(&stickyErr);
  510.             goto CantCreateDocName;
  511.         }
  512.     }
  513.     
  514.     /* Add the date and time shape to the page */
  515.     {
  516.         gxPoint            where;
  517.         Str255            s;
  518.         Str255            dateAndTime;
  519.         unsigned long    secs;
  520.  
  521.         /* Form the date/time string */
  522.         
  523.         GetDateTime(&secs);
  524.         iudatestring(secs, shortDate, (char *) dateAndTime);
  525.         BlockMove(" @ ", dateAndTime + strlen ((char *) dateAndTime), 4 );
  526.     
  527.         iutimestring(secs, false, (char *) s);
  528.         BlockMove( s, dateAndTime + strlen ((char *) dateAndTime), strlen ((char *) s) + 1 );
  529.         
  530.         /* Determine the position of the date/time string and create the text shape */
  531.         
  532.         where.y = pageBounds.top - ff(5);
  533.         where.x = pageBounds.left + ff(20);
  534.         
  535.         dateShape = GXNewText( strlen ((char *) dateAndTime), dateAndTime, &where);
  536.         if (dateShape != nil)    /* Created the new date item */
  537.         {
  538.             /* Set the font and its size */
  539.             GXSetShapeFont(dateShape, FindPNameFont(gxFullFontName, "\pHelvetica"));
  540.             GXSetShapeTextSize(dateShape, ff(12));
  541.  
  542.             /* Add the date/time shape to the page */
  543.             GXSetPictureParts( page, 0, 0, 1, &dateShape, nil, nil, &invxform );
  544.             GXDisposeShape(dateShape);
  545.         }
  546.         else
  547.         {    
  548.             anErr = GXGetGraphicsError(&stickyErr);
  549.  
  550.             GXDisposeShape(nameShape);
  551.  
  552.             goto CantCreateDate;
  553.         }
  554.     }
  555.  
  556.     /* Add the name of the machine on which the page is being printed */
  557.     {
  558.         gxPoint            where;
  559.         Str255            s;
  560.         Str255            name;
  561.  
  562.         BlockMove("Printed on ", s, 12);
  563.         
  564.         /* Form the machine name string */
  565.         {
  566.             StringHandle    userName;
  567.         
  568.             userName = (StringHandle) GetResource('STR ', kUserNameStrID);
  569.             if (userName != nil)    /* Got a name of some sort */
  570.             {
  571.                 /* Detach and convert to a C string */
  572.                 DetachResource((Handle) userName);
  573.                 p2cstr(*userName);
  574.                 
  575.                 if (**userName != '\0')    /* Some name is specified */
  576.                 {
  577.                     BlockMove(*userName, name, strlen ((char *) *userName) + 1);
  578.                 }
  579.                 else    /* Zero length name */
  580.                     BlockMove("Unknown", name, 8);
  581.  
  582.                 /* Dump the storage */
  583.                 DisposHandle((Handle) userName);
  584.             }
  585.             else
  586.                 BlockMove("Unknown", name, 8);
  587.         }
  588.  
  589.         /* Finish off the rest of the string - concatenate "'s Macintosh" */
  590.         BlockMove(name, s + strlen ((char *) s), strlen ((char *) name) + 1);
  591.         BlockMove("'s Macintosh", s + strlen ((char *) s), 13);
  592.         
  593.         /* Position and allocate the text shape */
  594.         
  595.         where.y = pageBounds.bottom + ff(13);
  596.         where.x = pageBounds.left + ff(20);
  597.         
  598.         userNameShape = GXNewText( strlen ((char *) s), s, &where);
  599.         if (userNameShape != nil)    /* Created the new date item */
  600.         {
  601.             /* Set the font and text size */
  602.             GXSetShapeFont(userNameShape, FindPNameFont(gxFullFontName, "\pSkia Regular"));
  603.             GXSetShapeTextSize(userNameShape, ff(12));
  604.  
  605.             /* Add the name shape to the page */
  606.             GXSetPictureParts( page, 0, 0, 1, &userNameShape, nil, nil, &invxform );
  607.             GXDisposeShape(userNameShape);
  608.  
  609.             {
  610.                 gxFontVariation        variations;
  611.                 
  612.                 variations.name = 'wght';
  613.                 variations.value = 0x00004000;
  614.                 
  615.                 GXSetStyleFontVariations(GXGetShapeStyle(userNameShape), 1, &variations);
  616.             }
  617.  
  618.         }
  619.         else
  620.         {    
  621.             anErr = GXGetGraphicsError(&stickyErr);
  622.  
  623.             GXDisposeShape(nameShape);
  624.             GXDisposeShape(dateShape);
  625.  
  626.             goto CantCreateUserName;
  627.         }
  628.     }
  629.     
  630.     GXDisposeTransform( invxform );
  631.     return(noErr);
  632.  
  633. /**************** Clean-up ****************/
  634.  
  635. CantScalePage:
  636. CantCreateBorderRect:
  637. CantCopyPageShape:
  638. CantCreateDocName:
  639. CantCreateDate:
  640. CantCreateUserName:
  641.  
  642.     return(anErr);
  643. }
  644. /* AddPageBorder */
  645.  
  646.  
  647. /*======================== INTERFACE ROUTINES FOR SERIALIZING DOCUMENT PAGES =======================*/
  648.  
  649.  
  650. /* ===== SerializeCopies =====
  651.  
  652.     SerializeCopies adds the serial number to each page which is printed.
  653. */
  654. void SerializeCopies(                    //    (out)    Error code
  655.     gxShape            page,                    //    (in)    picture for the page being spooled
  656.     gxFormat            pageFormat,            //    (in)    Page format record
  657.     long                currCopyNum,        //    (in)    Current copy of the document being printed (relative to beginning of the job)
  658.     Boolean            *pageChanged,        //    (out)    True if a copy of a page has changed; otherwise ignored
  659.     long                 nextEndSerialNum,    //    (in)    last serial number in the batch being printed
  660.     long                 numCopies)            //    (in)    number of copies being printed
  661. {
  662.     gxRectangle        pageRect;            // rectangle for the page.
  663.     gxPoint            where;                // location of the stamp on the page.
  664.     Fixed                dx, dy,
  665.                         cx, cy;
  666.     gxPoint            shapeCenter;
  667.     Str31                copyNumber;
  668.     gxRectangle        shapeRect;
  669.     Fixed                sx;
  670.     Fixed                sy;
  671.     Fixed                scale;
  672.     
  673.     /* First remove the last serial number shape which was added to the page the last */
  674.     /* time the page was rendered. */
  675.     if (gSerialShape != nil)
  676.     {
  677.         short        i;
  678.         short        numPictItems;
  679.         
  680.         numPictItems = GXGetPicture(page, nil, nil, nil, nil);
  681.         
  682.         for (i = 1; i <= numPictItems; i++)
  683.         {
  684.             gxShape        aShape;
  685.             
  686.             (void) GetPictureItem(page, i, &aShape, nil, nil, nil);
  687.             
  688.             if (gSerialShape == aShape)    /* The last serial number is already in the page; remove it */
  689.             {
  690.                 GXSetPictureParts(page, i, 1, 0, nil, nil, nil, nil);
  691.                 break;
  692.             }
  693.         }
  694.     }
  695.     
  696.     GXGetFormatDimensions(pageFormat, &pageRect, nil);
  697.     
  698.     where.x = pageRect.left;
  699.     where.y = pageRect.bottom;
  700.     
  701.     dx = pageRect.right - pageRect.left;
  702.     dy = pageRect.top - pageRect.bottom;
  703.     
  704.     cx = FixDiv(dx, ff(2)) + pageRect.left;
  705.     cy = FixDiv(dy, ff(2)) + pageRect.bottom;
  706.         
  707.     /* Compute the correct copy number and place string in the copyShape */
  708.     numtostring(nextEndSerialNum - numCopies + currCopyNum, (char *) copyNumber);
  709.     gSerialShape = GXNewText(strlen ((char *) copyNumber), copyNumber, &where);
  710.  
  711.     /* Set the text shape's font and size */
  712.     GXSetShapeFont(gSerialShape, FindPNameFont(gxFullFontName, "\pHelvetica"));
  713.     GXSetShapeTextSize(gSerialShape, ff(48));
  714.  
  715.     /* Center the shape on the page */    
  716.     GXGetShapeCenter(gSerialShape, 0, &shapeCenter);
  717.     GXMoveShape(gSerialShape, cx - shapeCenter.x, cy - shapeCenter.y);
  718.     
  719.     /* Draw the shape as an outline */
  720.     
  721.     GXSetShapeType(gSerialShape, gxPathType);
  722.     GXSetShapeFill(gSerialShape, gxClosedFrameFill);
  723.     GXSetShapePen(gSerialShape, 0x00005000);
  724.  
  725.     /* Now scale it so it fills most of the page */
  726.     {
  727.         gxTransform    theTransform;
  728.  
  729.         GXGetShapeBounds(gSerialShape, 0, &shapeRect);
  730.         sx = FixDiv(dx, shapeRect.right - shapeRect.left);
  731.         sy = FixDiv(dy, shapeRect.top - shapeRect.bottom);
  732.         
  733.         if (sx < sy)
  734.             scale = sx;
  735.         else
  736.             scale = sy;
  737.             
  738.         scale = FixMul(scale, 0x0000B500);                        // use 70% of the scale value;
  739.         
  740.         theTransform = GXGetShapeTransform(gSerialShape);
  741.         
  742.         GXScaleTransform(theTransform, scale, scale, cx, cy);
  743.     }
  744.         
  745.     /* Add the shape to the page and dispose of the local copy of the shape.  Also, */
  746.     /* save its reference so we can delete it the next time we add a serial number */
  747.     /* shape to this rendered page. */
  748.     
  749.     InsertPictureItem(page, 0, gSerialShape, nil, nil, nil);
  750.     GXDisposeShape(gSerialShape);
  751.  
  752.     (void) GetPictureItem(page, GXGetPicture(page, nil, nil, nil, nil), &gSerialShape, nil, nil, nil);
  753.         
  754.     /* Indicate we've changed a copy of the page */
  755.     *pageChanged = true;
  756. }
  757. /* SerializeCopies */
  758.